import { TimelinePointModel } from '@models/TimelineVerticalModel'; // Assuming model path
import cls from 'classnames'; // Utility for conditionally joining classNames
import React, {
  memo, // Import memo for component optimization
  useEffect,
  useMemo,
  useRef,
  FunctionComponent, // Explicit import
  MouseEvent,
} from 'react';
import {
  useTimelineStaticConfig,
  useTimelineMemoizedObjects,
} from '../contexts';
// Using shape from veShape import below
import {
  timelinePointContainer,
  timelinePointWrapper,
} from './timeline-vertical-shape.css';
import * as veShape from './timeline-vertical-shape.css'; // additive VE classes

/**
 * Renders the circular point or icon on the timeline line for a vertical item.
 * Handles click events, active state highlighting, custom icons, and appearance
 * based on global theme and configuration settings. It uses React.memo for
 * performance optimization.
 *
 * @param {TimelinePointModel} props - The properties for the TimelinePoint component.
 * @returns {React.ReactElement} The rendered TimelinePoint component.
 */
const TimelinePoint: FunctionComponent<TimelinePointModel> = memo(
  (props: TimelinePointModel) => {
    const {
      className, // 'left' or 'right' - passed to styled components
      id, // Unique ID of the timeline item
      onClick, // Callback function when the point is clicked
      active, // Is this point currently active/highlighted?
      onActive, // Callback function when the point should trigger the 'active' state calculation
      slideShowRunning, // Is a slideshow currently active? (disables onClick)
      iconChild, // Custom React node to display inside the point (replaces default shape)
      timelinePointDimension, // Size (width/height) of the point
      lineWidth, // Width of the timeline line connecting points
      disableClickOnCircle, // Should clicks on the point be ignored?
      cardLess, // Is the timeline in 'cardLess' mode?
      isMobile, // Is the view currently mobile?
    } = props;

    // Ref to the button element representing the point
    const circleRef = useRef<HTMLButtonElement>(null);

    // Access context settings
    const {
      focusActiveItemOnLoad,
      timelinePointShape,
      disableTimelinePoint,
      disableClickOnCircle: contextDisableClickOnCircle,
      disableInteraction: contextDisableInteraction,
      disableAutoScrollOnClick: contextDisableAutoScrollOnClick,
    } = useTimelineStaticConfig();

    const { theme, isDarkMode } = useTimelineMemoizedObjects();
    const { buttonTexts } = useTimelineMemoizedObjects();

    // Consolidated disable flags - prioritize props over context
    const finalDisableClickOnCircle =
      disableClickOnCircle ?? contextDisableClickOnCircle;
    const finalDisableInteraction = contextDisableInteraction;
    const finalDisableAutoScrollOnClick = contextDisableAutoScrollOnClick;

    // Ref to track if this is the component's first render cycle
    const isFirstRender = useRef(true);

    /**
     * Determines if the onActive callback should be invoked based on the active state
     * and whether it's the initial render (controlled by focusActiveItemOnLoad).
     */
    const canInvokeOnActive = useMemo(() => {
      // If focusing on load is enabled, invoke if active.
      if (focusActiveItemOnLoad) {
        return active;
      }
      // Otherwise, invoke only if active AND it's not the first render.
      else {
        return active && !isFirstRender.current;
      }
    }, [active, focusActiveItemOnLoad]); // Dependencies: active state and global setting

    /**
     * Effect to call the onActive callback when conditions are met.
     * This usually happens when an item scrolls into view or is programmatically activated.
     */
    useEffect(() => {
      if (canInvokeOnActive && onActive && circleRef.current) {
        // Call the parent's onActive handler with the point's offsetTop
        onActive(circleRef.current.offsetTop);
      }
      // Intentionally excluding onActive from dependencies if it's stable,
      // otherwise, include it if it might change. Usually, it's stable.
    }, [canInvokeOnActive, active]); // Re-run when activation condition or active state changes

    /**
     * Memoized CSS classes for the inner Shape component.
     * Applies 'active' class and 'using-icon' if a custom icon is provided.
     */
    const circleClass = useMemo(
      () =>
        cls({
          active: active, // Apply 'active' class if the point is active
          'using-icon': !!iconChild, // Apply class if a custom icon is used
        }),
      [active, iconChild], // Dependencies: active state and presence of iconChild
    );

    /**
     * Memoized click handler props for the TimelinePointContainer button.
     * Only adds onClick if clicks are enabled and slideshow isn't running.
     */
    const clickHandlerProps = useMemo(() => {
      // Return empty object (no click handler) if any disable condition is met
      if (
        finalDisableClickOnCircle ||
        finalDisableInteraction ||
        finalDisableAutoScrollOnClick
      ) {
        return {};
      }

      // Return props containing the onClick handler
      return {
        onClick: (ev: MouseEvent) => {
          ev.stopPropagation(); // Prevent event bubbling up
          // Call the provided onClick handler if it exists, passing the item ID
          if (id && onClick && !slideShowRunning) {
            onClick(id);
          }
        },
      };
    }, [
      id,
      onClick,
      slideShowRunning,
      finalDisableClickOnCircle,
      finalDisableInteraction,
      finalDisableAutoScrollOnClick,
    ]); // Dependencies for the click logic

    /**
     * Effect to update the isFirstRender flag after the initial render is complete.
     */
    useEffect(() => {
      // This effect runs only once after the initial mount
      if (isFirstRender.current) {
        isFirstRender.current = false;
      }
    }, []); // Empty dependency array ensures it runs only once

    // Create an accessible label for the timeline point
    const timelinePointLabel = useMemo(() => {
      return (
        buttonTexts?.timelinePoint ??
        (active ? 'Active timeline point' : 'Timeline point')
      );
    }, [active, buttonTexts]);

    return (
      <div
        className={`${className} ${timelinePointWrapper}`}
        style={{
          ...(isMobile ? { width: '25%' } : { width: '10%' }),
          ...(lineWidth !== undefined && {
            '--line-width': `${lineWidth}px`,
          }),
        }}
        data-testid="tree-leaf"
      >
        <button
          className={`${className} timeline-vertical-circle ${timelinePointContainer}`}
          {...clickHandlerProps}
          ref={circleRef}
          data-testid="tree-leaf-click"
          aria-label={timelinePointLabel}
          style={{
            ...(disableTimelinePoint && { visibility: 'hidden' }),
          }}
          aria-disabled={
            finalDisableClickOnCircle ||
            finalDisableInteraction ||
            finalDisableAutoScrollOnClick ||
            disableTimelinePoint
          }
          disabled={
            finalDisableClickOnCircle ||
            finalDisableInteraction ||
            finalDisableAutoScrollOnClick ||
            disableTimelinePoint
          }
          tabIndex={-1}
        >
          <div
            className={`${circleClass} ${veShape.shape} ${
              timelinePointShape === 'diamond' ? 'diamond' : ''
            } ${active ? 'active' : ''} ${iconChild ? 'using-icon' : ''}`}
            style={{
              ...(timelinePointDimension !== undefined && {
                width: `${timelinePointDimension}px`,
                height: `${timelinePointDimension}px`,
              }),
              // Theme colors are now applied via CSS variables from computeCssVarsFromTheme
            }}
            aria-hidden="true"
          >
            {iconChild}
          </div>
        </button>
      </div>
    ) as React.ReactElement;
  },
  // Use default shallow comparison for memoization.
  // The previous custom comparison (prev.active === next.active && prev.isMobile === next.isMobile)
  // was too restrictive and would prevent updates when other props like iconChild, theme, onClick, etc., changed.
  // Default shallow comparison is generally safer unless profiling reveals a specific need for a custom function.
);

// Set display name for React DevTools
TimelinePoint.displayName = 'TimelinePoint';

export { TimelinePoint }; // Export the component
